Skip to main content

Uniswap V3 闪贷

Uniswap V3 闪贷

Uniswap V3 Flash Loan

Uniswap V3 Flash Loan Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract UniswapV3Flash {
address private constant FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;

struct FlashCallbackData {
uint amount0;
uint amount1;
address caller;
}

IERC20 private immutable token0;
IERC20 private immutable token1;

IUniswapV3Pool private immutable pool;

constructor(address _token0, address _token1, uint24 _fee) {
token0 = IERC20(_token0);
token1 = IERC20(_token1);
pool = IUniswapV3Pool(getPool(_token0, _token1, _fee));
}

function getPool(
address _token0,
address _token1,
uint24 _fee
) public pure returns (address) {
PoolAddress.PoolKey memory poolKey = PoolAddress.getPoolKey(
_token0,
_token1,
_fee
);
return PoolAddress.computeAddress(FACTORY, poolKey);
}

function flash(uint amount0, uint amount1) external {
bytes memory data = abi.encode(
FlashCallbackData({amount0: amount0, amount1: amount1, caller: msg.sender})
);
IUniswapV3Pool(pool).flash(address(this), amount0, amount1, data);
}

function uniswapV3FlashCallback(
uint fee0,
uint fee1,
bytes calldata data
) external {
require(msg.sender == address(pool), "not authorized");

FlashCallbackData memory decoded = abi.decode(data, (FlashCallbackData));

// Repay borrow
if (fee0 > 0) {
token0.transferFrom(decoded.caller, address(this), fee0);
token0.transfer(address(pool), decoded.amount0 + fee0);
}
if (fee1 > 0) {
token1.transferFrom(decoded.caller, address(this), fee1);
token1.transfer(address(pool), decoded.amount1 + fee1);
}
}
}

library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH =
0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;

struct PoolKey {
address token0;
address token1;
uint24 fee;
}

function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
}

function computeAddress(
address factory,
PoolKey memory key
) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint160(
uint(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
}

interface IUniswapV3Pool {
function flash(
address recipient,
uint amount0,
uint amount1,
bytes calldata data
) external;
}

interface IERC20 {
function totalSupply() external view returns (uint);

function balanceOf(address account) external view returns (uint);

function transfer(address recipient, uint amount) external returns (bool);

function allowance(address owner, address spender) external view returns (uint);

function approve(address spender, uint amount) external returns (bool);

function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);

event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}

interface IWETH is IERC20 {
function deposit() external payable;

function withdraw(uint amount) external;
}

Test with Foundry

  1. Copy and paste this into test folder in your foundry project 将其复制并粘贴到test您的铸造项目的文件夹中
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "forge-std/console.sol";

import "../src/UniswapV3Flash.sol";

contract UniswapV3FlashTest is Test {
address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
uint24 constant POOL_FEE = 3000;

IWETH private weth = IWETH(WETH);
IERC20 private usdc = IERC20(USDC);

UniswapV3Flash private uni = new UniswapV3Flash(USDC, WETH, POOL_FEE);

function setUp() public {}

function testFlash() public {
// Approve WETH fee
weth.deposit{value: 1e18}();
weth.approve(address(uni), 1e18);

uint balBefore = weth.balanceOf(address(this));
uni.flash(0, 100 * 1e18);
uint balAfter = weth.balanceOf(address(this));

uint fee = balBefore - balAfter;
console.log("WETH fee", fee);
}
}
  1. 执行以下命令运行测试
FORK_URL=https://eth-mainnet.g.alchemy.com/v2/613t3mfjTevdrCwDl28CVvuk6wSIxRPi
forge test -vv --gas-report --fork-url $FORK_URL --match-path test/UniswapV3FlashTest.test.sol